// cn/iocoder/yudao/module/gold/service/transfer/GoldMaterialTransferServiceImpl.java
package cn.iocoder.yudao.module.system.service.warehouse;

import cn.hutool.core.util.IdUtil;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.warehouse.vo.*;
import cn.iocoder.yudao.module.system.dal.dataobject.warehouse.GoldMaterialRecordDO;
import cn.iocoder.yudao.module.system.dal.dataobject.warehouse.GoldMaterialTransferDO;
import cn.iocoder.yudao.module.system.dal.dataobject.warehouse.GoldMaterialWarehouseDO;
import cn.iocoder.yudao.module.system.dal.dataobject.warehouse.GoldMaterialWarehouseUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.warehouse.GoldMaterialRecordMapper;
import cn.iocoder.yudao.module.system.dal.mysql.warehouse.GoldMaterialTransferMapper;
import cn.iocoder.yudao.module.system.dal.mysql.warehouse.GoldMaterialWarehouseMapper;
import cn.iocoder.yudao.module.system.dal.mysql.warehouse.GoldMaterialWarehouseUserMapper;
import cn.iocoder.yudao.module.system.enums.GoldOperationSubTypeEnum;
import cn.iocoder.yudao.module.system.enums.GoldOperationTypeEnum;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import com.baomidou.mybatisplus.core.metadata.IPage;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;

import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;

@Service
@Validated
public class GoldMaterialTransferServiceImpl implements GoldMaterialTransferService {

    @Resource
    private GoldMaterialTransferMapper transferMapper;
    @Resource
    private GoldMaterialWarehouseMapper warehouseMapper;
    @Resource
    private GoldMaterialRecordMapper recordMapper;
    @Resource
    private GoldMaterialWarehouseUserMapper warehouseUserMapper;

    private String genNo() {
        return "GT" + IdUtil.getSnowflakeNextIdStr();
    }

    private void assertWH(GoldMaterialWarehouseDO w) {
        if (w == null) throw ServiceExceptionUtil.invalidParamException("仓库不存在");
        if (w.getStatus() != null && w.getStatus() == 0) throw ServiceExceptionUtil.invalidParamException("仓库禁用");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long create(TransferCreateReqVO req, Long uid, String uname) {
        GoldMaterialWarehouseDO to = warehouseMapper.selectById(req.getToWarehouseId());
        assertWH(to);
        if (req.getTransferType() == 1 && to.getType() == 3)
            throw ServiceExceptionUtil.invalidParamException("调拨→只能到仓库");
        if (req.getTransferType() == 2 && to.getType() != 3)
            throw ServiceExceptionUtil.invalidParamException("派发→只能到人员仓");

        // 还料操作：从个人仓到公司仓或团队仓
        if (req.getTransferType() == 3) { // 假设3表示还料
            GoldMaterialWarehouseDO from = warehouseMapper.selectById(req.getFromWarehouseId());
            if (from.getType() != 3) {
                throw ServiceExceptionUtil.invalidParamException("还料→只能从个人仓");
            }
            if (to.getType() == 3) {
                throw ServiceExceptionUtil.invalidParamException("还料→只能到公司仓或团队仓");
            }
        }
        // 验证调出仓库余额是否足够
        GoldMaterialWarehouseDO from = warehouseMapper.selectById(req.getFromWarehouseId());
        if (from.getBalance().compareTo(req.getTransferWeight()) < 0) {
            throw ServiceExceptionUtil.invalidParamException("调出仓库余额不足，当前余额：{}克，需要：{}克", from.getBalance(), req.getTransferWeight());
        }

        //查询审核人id
        GoldMaterialWarehouseUserDO goldMaterialWarehouseUserDO = getGoldMaterialWarehouseUserDO(req.getToWarehouseId());
        GoldMaterialTransferDO t = GoldMaterialTransferDO.builder()
                .transferType(req.getTransferType()).transferNo(genNo()).creator(req.getCreator())
                .fromWarehouseId(req.getFromWarehouseId()).toWarehouseId(req.getToWarehouseId())
                .transferWeight(req.getTransferWeight()).transferReason(req.getTransferReason())
                .status(0).attachmentUrl(req.getAttachmentUrl()).approveRemark(uname+"-"+req.getRemark())
                .tenantId(1L).approverId(goldMaterialWarehouseUserDO.getUserId()).build();
        transferMapper.insert(t);
        return t.getId();
    }

    //根据仓库id查询对应负责人userid
    private GoldMaterialWarehouseUserDO getGoldMaterialWarehouseUserDO(Long id) {
        LambdaQueryWrapperX<GoldMaterialWarehouseUserDO> wrapperX = new LambdaQueryWrapperX<GoldMaterialWarehouseUserDO>()
                .eq(GoldMaterialWarehouseUserDO::getWarehouseId, id)
                .eq(GoldMaterialWarehouseUserDO::getDeleted, false);
        GoldMaterialWarehouseUserDO goldMaterialWarehouseUserDO = warehouseUserMapper.selectOne(wrapperX);
        return goldMaterialWarehouseUserDO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long transfer(TransferCreateReqVO reqVO, Long uid, String uname) {
        // 业务约束：目标不能是人员仓
        GoldMaterialWarehouseDO from = warehouseMapper.selectById(reqVO.getFromWarehouseId());
        GoldMaterialWarehouseDO to = warehouseMapper.selectById(reqVO.getToWarehouseId());
        assertWH(from);
        assertWH(to);
        if (to.getType() == 3) {
            throw ServiceExceptionUtil.invalidParamException("调拨目标必须是仓库");
        }
        BigDecimal w = reqVO.getTransferWeight();
        if (from.getBalance().compareTo(w) < 0) {
            throw ServiceExceptionUtil.invalidParamException("调出仓库余额不足，当前余额：{}克，需要：{}克", from.getBalance(), w);
        }

        GoldMaterialTransferDO t = GoldMaterialTransferDO.builder()
                .transferType(1).transferNo(genNo()).creator(reqVO.getCreator())
                .fromWarehouseId(reqVO.getFromWarehouseId()).toWarehouseId(reqVO.getToWarehouseId())
                .transferWeight(w).transferReason(reqVO.getTransferReason())
                .status(1).attachmentUrl(reqVO.getAttachmentUrl()).approveRemark(reqVO.getRemark()).tenantId(1L).build();
        transferMapper.insert(t);

        // 余额变更
        BigDecimal fromInit = from.getBalance();
        BigDecimal toInit = to.getBalance();
        warehouseMapper.updateBalanceDelta(from.getId(), w.negate());
        warehouseMapper.updateBalanceDelta(to.getId(), w);

        // 流水
        BigDecimal fromFin = fromInit.subtract(w);
        BigDecimal toFin = toInit.add(w);
        recordMapper.insert(GoldMaterialRecordDO.builder()
                .recordNo(t.getTransferNo()).warehouseId(from.getId())
                .operationType(GoldOperationTypeEnum.OUT.getType()).operationSubtype(6)
                .initialWeight(fromInit).operationWeight(w).finalWeight(fromFin)
                .operatorId(uid).operatorName(uname)
                .remark(reqVO.getTransferReason()).attachmentUrl(reqVO.getAttachmentUrl())
                .relatedBusinessNo(t.getTransferNo()).relatedParty(warehouseMapper.selectById(from.getId()).getName())
                .creator(reqVO.getCreator()).businessType("调拨出料").status(1).createTime(LocalDateTime.now()).build());

        recordMapper.insert(GoldMaterialRecordDO.builder()
                .recordNo(t.getTransferNo()).warehouseId(to.getId())
                .operationType(GoldOperationTypeEnum.IN.getType()).operationSubtype(3)
                .initialWeight(toInit).operationWeight(w).finalWeight(toFin)
                .operatorId(uid).operatorName(uname)
                .remark(reqVO.getTransferReason()).attachmentUrl(reqVO.getAttachmentUrl())
                .relatedBusinessNo(t.getTransferNo()).relatedParty(warehouseMapper.selectById(from.getId()).getName())
                .creator(reqVO.getCreator()).businessType("调拨入料").status(1).createTime(LocalDateTime.now()).build());

        return t.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long dispatch(TransferCreateReqVO reqVO, Long uid, String uname) {
        // 业务约束：目标必须是人员仓
        GoldMaterialWarehouseDO from = warehouseMapper.selectById(reqVO.getFromWarehouseId());
        GoldMaterialWarehouseDO to = warehouseMapper.selectById(reqVO.getToWarehouseId());
        assertWH(from);
        assertWH(to);
        if (to.getType() != 3) {
            throw ServiceExceptionUtil.invalidParamException("派发目标必须是人员仓");
        }
        BigDecimal w = reqVO.getTransferWeight();
        if (from.getBalance().compareTo(w) < 0) {
            throw ServiceExceptionUtil.invalidParamException("来源余额不足");
        }

        GoldMaterialTransferDO t = GoldMaterialTransferDO.builder()
                .transferType(2).transferNo(genNo()).creator(reqVO.getCreator())
                .fromWarehouseId(reqVO.getFromWarehouseId()).toWarehouseId(reqVO.getToWarehouseId())
                .transferWeight(w).transferReason(reqVO.getTransferReason())
                .status(1).attachmentUrl(reqVO.getAttachmentUrl()).approveRemark(reqVO.getRemark()).tenantId(1L).build();
        transferMapper.insert(t);

        BigDecimal fromInit = from.getBalance();
        BigDecimal toInit = to.getBalance();
        warehouseMapper.updateBalanceDelta(from.getId(), w.negate());
        warehouseMapper.updateBalanceDelta(to.getId(), w);

        BigDecimal fromFin = fromInit.subtract(w);
        BigDecimal toFin = toInit.add(w);
        recordMapper.insert(GoldMaterialRecordDO.builder()
                .recordNo(t.getTransferNo()).warehouseId(from.getId())
                .operationType(GoldOperationTypeEnum.OUT.getType()).operationSubtype(7)
                .initialWeight(fromInit).operationWeight(w).finalWeight(fromFin)
                .operatorId(uid).operatorName(uname)
                .remark(reqVO.getTransferReason()).attachmentUrl(reqVO.getAttachmentUrl())
                .relatedBusinessNo(t.getTransferNo()).relatedParty(warehouseMapper.selectById(from.getId()).getName())
                .creator(reqVO.getCreator()).businessType("派发出料").status(1).createTime(LocalDateTime.now()).build());

        recordMapper.insert(GoldMaterialRecordDO.builder()
                .recordNo(t.getTransferNo()).warehouseId(to.getId())
                .operationType(GoldOperationTypeEnum.IN.getType()).operationSubtype(4)
                .initialWeight(toInit).operationWeight(w).finalWeight(toFin)
                .operatorId(uid).operatorName(uname)
                .remark(reqVO.getTransferReason()).attachmentUrl(reqVO.getAttachmentUrl())
                .relatedBusinessNo(t.getTransferNo()).relatedParty(warehouseMapper.selectById(from.getId()).getName())
                .creator(reqVO.getCreator()).businessType("派发入料").status(1).createTime(LocalDateTime.now()).build());

        return t.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approve(TransferApproveReqVO req, Long approverId, String approverName) {
        GoldMaterialTransferDO t = transferMapper.selectByIdForUpdate(req.getId());
        if (t == null || t.getStatus() != 0) throw ServiceExceptionUtil.invalidParamException("单据不存在或已处理");
        if (req.getDecision() == 2) {
            if (transferMapper.updateApprove(t.getId(), 2, approverId,t.getApproveRemark()+ "-" + req.getApproveRemark()) == 0)
                throw ServiceExceptionUtil.invalidParamException("拒绝失败");
            return;
        }
        Long a = Math.min(t.getFromWarehouseId(), t.getToWarehouseId());
        Long b = Math.max(t.getFromWarehouseId(), t.getToWarehouseId());
        GoldMaterialWarehouseDO wA = warehouseMapper.selectByIdForUpdate(a);
        GoldMaterialWarehouseDO wB = warehouseMapper.selectByIdForUpdate(b);
        GoldMaterialWarehouseDO from = t.getFromWarehouseId().equals(a) ? wA : wB;
        GoldMaterialWarehouseDO to = t.getToWarehouseId().equals(a) ? wA : wB;
        assertWH(from);
        assertWH(to);

        // 业务类型验证
        if (t.getTransferType() == 1 && to.getType() == 3)
            throw ServiceExceptionUtil.invalidParamException("调拨目标必须是仓库");
        if (t.getTransferType() == 2 && to.getType() != 3)
            throw ServiceExceptionUtil.invalidParamException("派发目标必须是人员仓");
        if (t.getTransferType() == 3) {
            // 还料操作：从个人仓到公司仓或团队仓
            if (from.getType() != 3) {
                throw ServiceExceptionUtil.invalidParamException("还料→只能从个人仓");
            }
            if (to.getType() == 3) {
                throw ServiceExceptionUtil.invalidParamException("还料→只能到公司仓或团队仓");
            }
        }

        BigDecimal w = t.getTransferWeight();
        BigDecimal fromInit = from.getBalance();
        if (warehouseMapper.updateBalanceDelta(from.getId(), w.negate()) == 0)
            throw ServiceExceptionUtil.invalidParamException("来源余额不足");
        BigDecimal fromFin = fromInit.subtract(w);

        BigDecimal toInit = to.getBalance();
        if (warehouseMapper.updateBalanceDelta(to.getId(), w) == 0)
            throw ServiceExceptionUtil.invalidParamException("目标更新失败");
        BigDecimal toFin = toInit.add(w);

        // 根据操作类型确定子类型和业务类型
        int outSubtype, inSubtype;
        String outBusinessType, inBusinessType;

        if (t.getTransferType() == 1) {
            // 调拨
            outSubtype = 6; // 调拨出料
            inSubtype = 3;  // 调拨入料
            outBusinessType = "调拨出料";
            inBusinessType = "调拨入料";
        } else if (t.getTransferType() == 2) {
            // 派发
            outSubtype = 7; // 派发出料
            inSubtype = 4;  // 派发入料
            outBusinessType = "派发出料";
            inBusinessType = "派发入料";
        } else if (t.getTransferType() == 3) {
            // 还料
            outSubtype = 9; // 还料出料
            inSubtype = 10; // 还料入料
            outBusinessType = "还料出料";
            inBusinessType = "还料入料";
        } else {
            throw ServiceExceptionUtil.invalidParamException("未知的操作类型");
        }

        String name = warehouseMapper.selectById(t.getFromWarehouseId()).getName();

        // 生成出料记录
        GoldMaterialWarehouseUserDO goldMaterialWarehouseUserDO = getGoldMaterialWarehouseUserDO(from.getId());
        recordMapper.insert(GoldMaterialRecordDO.builder()
                .recordNo(t.getTransferNo()).warehouseId(from.getId())
                .operationType(GoldOperationTypeEnum.OUT.getType()).operationSubtype(outSubtype)
                .initialWeight(fromInit).operationWeight(w).finalWeight(fromFin)
                .operatorId(goldMaterialWarehouseUserDO.getUserId())
                .operatorName(t.getCreator())
                .remark(t.getTransferReason())
                .attachmentUrl(t.getAttachmentUrl())
                .relatedBusinessNo(t.getTransferNo())
                .relatedParty(name).creator(t.getCreator())
                .businessType(outBusinessType)
                .status(1).createTime(LocalDateTime.now()).build());

        // 生成入料记录
        recordMapper.insert(GoldMaterialRecordDO.builder()
                .recordNo(t.getTransferNo()).warehouseId(to.getId())
                .operationType(GoldOperationTypeEnum.IN.getType()).operationSubtype(inSubtype)
                .initialWeight(toInit).operationWeight(w).finalWeight(toFin)
                .operatorId(approverId).operatorName(approverName)
                .remark(t.getTransferReason())
                .attachmentUrl(t.getAttachmentUrl())
                .relatedBusinessNo(t.getTransferNo())
                .relatedParty(name).creator(t.getCreator())
                .businessType(inBusinessType)
                .status(1).createTime(LocalDateTime.now()).build());

        if (transferMapper.updateApprove(t.getId(), 1, approverId,t.getApproveRemark()+ "-" + req.getApproveRemark()) == 0)
            throw ServiceExceptionUtil.invalidParamException("单据状态更新失败");
    }

    @Override
    public PageResult<GoldMaterialTransferDO> getTransferPage(GoldMaterialTransferPageReqVO reqVO) {

        PageResult<GoldMaterialTransferDO> pageResult = transferMapper.selectPage(reqVO);

        return pageResult;
    }


}